const vs readonly — Deep Dive

The Core Difference

const

Compile-time constant

Compiler knows the value

Implicitly static
 
readonly

Runtime value

Assigned during initialization/construction

Cannot be reassigned afterward

Memory

const → compiler knows the value
readonly → runtime can set it, but cannot reassign it afterward

constreadonly
Java equivalentstatic final (compile-time constant)final
Known atCompile timeRuntime
AssignmentDeclaration onlyDeclaration / constructor
Instance field
Implicitly static
Runtime values
Can reassign later?
Typical useTrue constantsObject state / DI dependencies

private vs readonly

These solve different problems:

private vs readonly

private controls who can access the field.
readonly controls whether the field can be reassigned.

private string _name;          // can be changed internally
private readonly string _name; // cannot be reassigned after construction

Therefore:

private readonly string _connectionString;

means:

Only this class can access it, and it can only be assigned during initialization/construction.

Java → C#

private final String name;  →  private readonly string _name;
 
static final int MAX = 10;  →  const int Max = 10;
 
static final Guid id =      →  static readonly Guid Id =
    UUID.randomUUID();          Guid.NewGuid();

Getter vs Setter

Read-only property

public string Name { get; }
 
public User(string name)
{
    Name = name;
}

Outside code:

user.Name;       // ✅
user.Name = "X"; // ❌

Good when the value should never change after construction.

Internally mutable property

public string Name { get; private set; }
 
public User(string name)
{
    Name = name;
}

Outside:

user.Name = "X"; // ❌

Inside the class:

Name = "X"; // ✅

Don't confuse these

get; = publicly readable, not necessarily a readonly field.

get; private set; = outside cannot modify, but the class can.

readonly field = field cannot be reassigned after construction.

Why readonly is useful in ASP.NET Core

Common dependency-injection pattern:

public class OrderService
{
    private readonly IOrderRepository _repository;
 
    public OrderService(IOrderRepository repository)
    {
        _repository = repository;
    }
}

The dependency is established when the object is created and should not be replaced later.

Production Rule

Use:

const
→ value is genuinely a compile-time invariant
 
readonly
→ value comes from runtime state but should not be reassigned
 
get; private set;
→ class needs to control future mutations
 
get;
→ value should be established during construction and then exposed read-only

Public const and versioning

const values can be embedded into consuming assemblies at compile time. Changing a public constant in a library may therefore require consumers to be recompiled.

static readonly is preferable when the value is runtime-initialized or may change independently of consumers.

readonly ≠ deep immutability

private readonly List<string> _items = new();
_items.Add("A"); // ✅
_items = new();  // ❌

readonly prevents reassignment, not mutation of the referenced object.